﻿using System;
using System.Collections.Generic;

namespace Synergy
{
	/// <summary>
	/// Lightweight service locator for dependency injection within a single project.
	/// </summary>
	/// <remarks>
	///		<list type="table">
	///			<listheader><term>Version</term><description>Description</description></listheader>
	///			<item>
	///				<term>1.0</term>
	///				<description>Initial version</description>
	///			</item>
	///		</list>
	/// </remarks>
	public class LightServiceLocator
	{
		private readonly Dictionary<Type, object> _container = new Dictionary<Type, object>();


		/// <summary>
		/// Registers the specified <paramref name="instance"/> to be returned when type <typeparamref name="T"/> is accessed.
		/// </summary>
		/// <typeparam name="T">The type of the lookup.</typeparam>
		/// <param name="instance">The instance to register.</param>
		/// <returns>The current instance of <see cref="LightServiceLocator"/> for chaining.</returns>
		public LightServiceLocator Register<T>(T instance)
		{
			if (this._container.ContainsKey(typeof(T)))
			{
				this._container[typeof(T)] = instance;
			}
			else
			{
				this._container.Add(typeof(T), instance);
			}
			return this;
		}


		/// <summary>
		/// Registers the type <typeparamref name="TInstance"/> to be instantiated when type <typeparamref name="TLookup"/> is accessed.
		/// </summary>
		/// <typeparam name="TLookup">The type of the lookup.</typeparam>
		/// <typeparam name="TInstance">The type of the instance to create.</typeparam>
		/// <returns>The current instance of <see cref="LightServiceLocator"/> for chaining.</returns>
		/// <remarks>
		/// Code analysis message CA1004 (Generic Methods Should Provide Type Parameter) is being suppressed here
		/// because type-resolution must be explicit.
		/// </remarks>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
		public LightServiceLocator Register<TLookup, TInstance>() where TInstance : TLookup
		{
			if (this._container.ContainsKey(typeof(TLookup)))
			{
				this._container[typeof(TLookup)] = typeof(TInstance);
			}
			else
			{
				this._container.Add(typeof(TLookup), typeof(TInstance));
			}
			return this;
		}


		/// <summary>
		/// Looks up the registered instance based on type <see cref="T"/>.
		/// </summary>
		/// <typeparam name="T">The type to lookup.</typeparam>
		/// <remarks>
		///     Code analysis message CA1004 (Generic Methods Should Provide Type Parameter) is being suppressed here 
		///     because type-resolution must be explicit.
		/// </remarks>
		/// <returns>The registered instance of type <typeparamref name="T"/>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
		public T Lookup<T>()
		{
			var registered = this._container.ContainsKey(typeof(T)) ? this._container[typeof(T)] : null;
			if (registered == null) { return default(T); }

			var type = registered as Type;
			return (type == null) ? (T)registered : (T)Activator.CreateInstance(type);
		}
	}
}